/**
 * 使用 Ring 实现的 Console
 */

#include <stdint.h>
#include <string.h>

#include "utils.h"
#include "utils.hpp"
#include "transceiver.h"

#include "log.h"
#define LOGLEVEL LOG_DEBUG
#define LOGTAG   "OBS900"
#include "log4app.h"

std_err_t transceiver::rx_ret_to_std_err(rx_ret_t ret) {
	if (ret <= RX_RET_ERROR)    return (std_err_t)ret;
	if (ret == RX_RET_FULL)     return STD_ERR_BUFFER_OVERFLOW;
	if (ret == RX_RET_TIMEOUT)  return STD_ERR_TIMEOUT;
	if (ret == RX_RET_INTERVAL) return STD_ERR_INTERVAL_TIMEOUT;
	if (ret == RX_RET_OK)       return STD_ERR_OK;
	if (ret == RX_RET_FAIL)     return STD_ERR_INVALID_RESPONSE;
	return STD_ERR_FAIL;
}

const char* transceiver::rx_ret_comment(rx_ret_t ret) {
	switch(ret) {
	case RX_RET_FULL: return "RX_RET_FULL";
	case RX_RET_TIMEOUT: return "RX_RET_TIMEOUT";
	case RX_RET_INTERVAL: return "RX_RET_INTERVAL";
	case RX_RET_OK: return "RX_RET_OK";
	case RX_RET_FAIL: return "RX_RET_FAIL";
	default: return std_err_comment(ret);
	}
}

#if (defined _WIN32) || __linux__ || __unix__
    // 在 windows/linux/unix 下 默认使用 pcre2 实现的正则匹配功能
    extern "C" int regexp_match(const char* str, const char* pattern);
#endif

transceiver::transceiver(regexp_match_t regexp_match_func) {
    this->_regexp_match = regexp_match_func;
#if (defined _WIN32) || __linux__ || __unix__
    // 在 windows/linux/unix 下 默认使用 pcre2 实现的正则匹配功能
    if(!regexp_match_func) this->_regexp_match = regexp_match;
#endif

    _trx.hif             = this;
    _trx.get_mtu         = transceiver::_trx_get_mtu;
    _trx.clear_rx_buffer = transceiver::_trx_clear_rx_buffer;
    _trx.rx              = transceiver::_trx_rx;
    _trx.rxline          = transceiver::_trx_rxline;
    _trx.tx              = transceiver::_trx_tx;
    _trx.close           = transceiver::_trx_close;
    _trx.listen          = transceiver::_trx_listen;
    _trx.on_rx_data      = NULL;
    _trx.on_tx_empty     = NULL;
    _trx.regexp_match    = this->_regexp_match;
    _trx.ref_count       = 0;
}

std_err_t transceiver::rxline(void* _buf, size_t size, size_t *plen) {
    if(!_buf || !size) return (rx_ret_t)STD_ERR_INVALID_ARG;
    std_err_t err = STD_ERR_OK;
    char *buf = (char*)_buf;

    // 阻塞式接收开始
    _wait4ack = true;

    do {
        // 永久等待发数据过来
        // !!! 注意：任务将在此阻塞 !!!
        err = rxwait(-1);
        if(err) break;

        // 接收数据(少收1个字节，以便加 '\0')
        err = rxread(buf, size-1, plen);
        if(err) break;
        buf[(*plen)] = '\0';

        if(buf[(*plen) - 1] == '\n') {
            // OK: 接收数据的最后一个字符是换行符，结束接收
        } else {
            // 没有行结束符，是因为行太长了？
            loge("Too long line ?\n");
            err = STD_ERR_BUFFER_OVERFLOW;
        }
    } while (0);

    // 阻塞式接收结束
    _wait4ack = false;
    return err;
}

rx_ret_t transceiver::rx(void* buf, size_t size, size_t *plen/*out*/, long total_timeout_ms, long lead_timeout_ms, long interval_timeout_ms, const void *okFlag, rx_flag_t okFlagType, const void *failFlag, rx_flag_t failFlagType) {
    if(!buf || !size) return (rx_ret_t)STD_ERR_INVALID_ARG;
    if((okFlagType == RX_FLAG_REGEXP || failFlagType == RX_FLAG_REGEXP) && !_regexp_match) return (rx_ret_t)STD_ERR_NOT_SUPPORTED;

    // 阻塞式接收开始
    _wait4ack = true;

    std_err_t err;
    rx_ret_t ret = RX_RET_TIMEOUT;
    char* data = (char*)buf;
    // 接收数据长度，当前帧数据长度
    size_t len = 0, slen = 0;
    // 开始接收时间，最新数据到达时间
    size_t start, last_byte_time = 0;
    // 默认超时 5 秒
    long remain_time = (total_timeout_ms > 0) ? total_timeout_ms : 5000; 
    long elapsed_time = 0;
    bool by_interval_timeout = 0;

    while(1) {
        // 获取起始等待时间
        start = unios_ms_now();

        // 不管有没有数据，都先尝试获取缓冲区中当前的数据
        err = rxread(data + len, size - len, &slen);
        if (err) {
            ret = (rx_ret_t)err;
            break;
        }
        if (slen) {
            len += slen;

            if (len >= size) {
                // 因缓冲区满中止
                ret = RX_RET_FULL;
                //if(dbgflag_get(0)) logd("c2h rx ok\n");
                break;
            }
            // 设置字符串结束标志
            data[len] = '\0';

            if (okFlag && okFlagType != RX_FLAG_NULL) {
                // 需要考虑 okFlag
                //puts(data);
                if ((okFlagType == RX_FLAG_STRING && strstr(data, (const char*)okFlag)) ||
                    (okFlagType == RX_FLAG_REGEXP && _regexp_match && _regexp_match(data, (const char*)okFlag) >= 0) ||
                    (okFlagType >= RX_FLAG_BINARY && memmem(data, len, (const char*)okFlag, okFlagType)))
                {
                    // 因 OK 标志中止
                    ret = RX_RET_OK;
                    break;
                }
            }

            if (failFlag && failFlagType != RX_FLAG_NULL) {
                // 需要考虑 failFlag
                if ((failFlagType == RX_FLAG_STRING && strstr(data, (const char*)failFlag)) ||
                    (failFlagType == RX_FLAG_REGEXP && _regexp_match && _regexp_match(data, (const char*)failFlag) >= 0) ||
                    (failFlagType >= RX_FLAG_BINARY && memmem(data, len, (const char*)failFlag, failFlagType)))
                {
                    // 因 Fail 标志中止
                    ret = RX_RET_FAIL;
                    break;
                }
            }
        }

        // 计算剩余总超时时间
        elapsed_time = (long)unios_ms_elapsed(start);
        if (remain_time > elapsed_time) {
            remain_time = remain_time - elapsed_time;
        } else {
            // 因总超时中止
            ret = RX_RET_TIMEOUT;
            break;
        }

        if (interval_timeout_ms > 0) {
            // 需要考虑间隔超时
            // 如果间隔超时小于总超时要求，以间隔超时为准
            if (last_byte_time && interval_timeout_ms < remain_time) {
                remain_time = interval_timeout_ms;
                by_interval_timeout = true;
            }
            // 记录最新数据到达时间
            if(slen) last_byte_time = unios_ms_now();
        }

        // 缓冲区未满，也未匹配到任何标志，也未超时，等待数据过来，直至超时
        // !!! 注意：任务将在此阻塞 !!!
        err = rxwait(remain_time);
        if(err == STD_ERR_OK) {
            // 收到数据
            continue;
        } else if(err != STD_ERR_TIMEOUT) {
            // 因内部错误中止
            ret = (rx_ret_t)err;
            break;
        } else {
            // 等待信号超时了
            // 即使超时也尝试获取数据？
            err = rxread(data + len, size - len, &slen);
            if (err) {
                ret = (rx_ret_t)err;
                break;
            }
            len += slen;
            if (len && len < size) {
                data[len] = '\0';
                //puts(data);
            }

            // 判断超时原因
            if (by_interval_timeout) {
                // 因间隔超时中止
                ret = RX_RET_INTERVAL;
            } else {
                // 因总超时中止
                ret = RX_RET_TIMEOUT;
            }
            break;
        }
    }

    // 阻塞式接收结束
    _wait4ack = false;
    if(plen) *plen = len;
    return ret;
}

rx_ret_t transceiver::cmdack(const char *cmd, char *rxbuf, size_t rxbuf_size, long timeout, const char *okFlag, const char *failFlag) {
    rxclear(); // 清除接收缓冲区
    
	// 发送指令
    std_err_t err = tx(cmd, strlen(cmd), NULL);
    if(err) return (rx_ret_t)err;

    // 接收返回数据
    size_t dlen = 0;
	rx_flag_t okFlagType = RX_FLAG_STRING, failFlagType = RX_FLAG_STRING;
	if(okFlag   && okFlag[0]   == '@') { okFlagType   = RX_FLAG_REGEXP; okFlag++;   }
	if(failFlag && failFlag[0] == '@') { failFlagType = RX_FLAG_REGEXP; failFlag++; }
    rx_ret_t ret = rx(rxbuf, rxbuf_size, &dlen, timeout, -1, -1, okFlag, okFlagType, failFlag, failFlagType);
    rxbuf[dlen] = '\0';
    return ret;
}

std_err_t transceiver_by_ring::txwrite(const void* buf, uint64_t size, size_t *plen/*out*/) {
    sync(); // 同步一次状态
    if(!buf || !size || size > txsize()) return STD_ERR_INVALID_ARG;
    uint8_t* src = (uint8_t*)buf;
    std_err_t err;
    if(plen) (*plen) = 0;

    // !!! 注意：循环检查缓冲区是否有足够空间，如果没有，则等待 1ms 后继续检查，直至 10ms 超时后返回错误 !!!
    for (int t = 10; size > txfree(); t--) {
        if(t <= 0) {
            // 强制清除发送缓冲区?
            loge("Bad state: transceiver TX ring keeps full ? Forcibly clear TX ring and try ...\n");
            txclear();
            break;
        }
        unios_us_delay(1000);
        sync(); // 状态同步
    }

    uint64_t tail = txtail();
    // 计算要写入尾部的数据长度
    size_t wtsize = (txsize() - tail);
    if(size < wtsize) wtsize = size;
    if(wtsize) {
        // 写入尾部
        err = _ftxring->write(txbase() + tail, src, wtsize, NULL);
        if(err) return err;
        if(plen) (*plen) += wtsize;
        src += wtsize;
    }

    // 计算要写入头部的数据长度
    wtsize = size - wtsize;
    if(!wtsize) {
        // 设置 tail 到自己已经写入完成的位置
        txtail((tail + size) % txsize());
    } else {
        // 写入头部
        err = _ftxring->write(txbase(), src, wtsize, NULL);
        if(err) return err;
        if(plen) (*plen) += wtsize;
        // 设置 tail 到自己已经写入完成的位置
        txtail(wtsize % txsize());
    }

    return STD_ERR_OK;
}

std_err_t transceiver_by_ring::rxread(void* buf, size_t size, size_t *plen/*out*/) {
    sync(); // 同步一次状态
    if(!buf || !size || size > rxsize()) return STD_ERR_INVALID_ARG;
    uint8_t* dst = (uint8_t*)buf;
    uint64_t dlen = rxdlen();
    std_err_t err;
    if(plen) (*plen) = 0;

    // 缓冲区中没有数据，直接返回
    if(!dlen) return STD_ERR_OK;
    // 读取长度不超时当前缓冲区中已有数据长度
    if(size > dlen) size = dlen;
    
    uint64_t head = rxhead();
    // 计算要从头部读取的数据长度
    size_t rdsize = (rxsize() - head);
    if(size < rdsize) rdsize = size;
    if(rdsize) {
        // 读取头部
        err = _frxring->read(rxbase() + head, dst, rdsize, NULL);
        if(err) return err;
        if(plen) (*plen) += rdsize;
        dst += rdsize;
    }
    
    // 计算要从尾部读取的数据长度
    rdsize = size - rdsize;
    if(!rdsize) {
        // 设置头到自己已经读取完成的位置
        rxhead((head + size) % rxsize());
    } else {
        // 读取尾部
        err = _frxring->read(rxbase(), dst, rdsize, NULL);
        if(err) return err;
        if(plen) (*plen) += rdsize;
        // 设置头到自己已经读取完成的位置
        rxhead(rdsize % rxsize());
    }
    
    return STD_ERR_OK;
}
